/*
 * Copyright (c) 2009 Roman Savelyev
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "conuisurface.h"
#include "conuiengine.h"
#include "conuisurfacelogger.h"

static ConUiDef::TColor TranslateColor(TColor aColor)
    {
    switch (aColor)
        {
        case EColorDefault:
            return ConUiDef::EDefault;
        case EColorBlack:
            return ConUiDef::EBlack;
        case EColorRed:
            return ConUiDef::ERed;
        case EColorGreen:
            return ConUiDef::EGreen;
        case EColorYellow:
            return ConUiDef::EYellow;
        case EColorBlue:
            return ConUiDef::EBlue;
        case EColorMagenta:
            return ConUiDef::EMagenta;
        case EColorCyan:
            return ConUiDef::ECyan;
        case EColorWhite:
            return ConUiDef::EWhite;
        default:
            ASSERT(EFalse);
        case EColorPreserve:
            User::Invariant();
            return ConUiDef::EDefault;
        }
    }
///////////////////////////////////////////////////////////////////////////////
EXPORT_C CConUiSurface* CConUiSurface::NewL(CConUiEngine& aEngine)
    {
    FL(_L("CConUiSurface::NewL"));
    CConUiSurface* self = new(ELeave) CConUiSurface(aEngine);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

CConUiSurface::CConUiSurface(CConUiEngine& aEngine) :
    iEngine(aEngine),
    iValidateCallback(CActive::EPriorityIdle)
    {}

void CConUiSurface::ConstructL()
    {
    FL(_L("CConUiSurface::ConstructL"));
    iBuffer = CDrawingBuffer::NewL(iEngine.SurfaceSize());
    iBlinker = CBlinker::NewL(*this);
    }

EXPORT_C CConUiSurface::~CConUiSurface()
    {
    FL(_L("CConUiSurface::~CConUiSurface"));
    delete iBlinker;
    delete iBuffer;
    iValidateCallback.Cancel();
    }

EXPORT_C const TRect& CConUiSurface::Rect() const
    {
    return iBuffer->Rect();
    }

EXPORT_C void CConUiSurface::Clear(const TRect& aRect)
    {
    FL(_L("CConUiSurface::Clear"));
    TRect inBounds = Rect();
    inBounds.Intersection(aRect);
    iBuffer->FillBackground(inBounds, ConUiDef::EDefault);
    iBuffer->FillForeground(aRect, ConUiDef::EDefault);
    iBuffer->FillBold(aRect, EFalse);
    iBuffer->FillBlinking(aRect, EFalse);
    iBuffer->FillInverseVideo(aRect, EFalse);
    iBuffer->ClearText(aRect);
    }

EXPORT_C void CConUiSurface::Draw(const TPoint& aAt, const TDesC& aText)
    {
    FL(_L("CConUiSurface::Draw"));
    if (Rect().Contains(aAt))
        {
        TInt length = Rect().iBr.iX - aAt.iX;
        TRect drawRect(aAt, aAt);
        drawRect.Grow(Min(Rect().iBr.iX - aAt.iX, aText.Length()), 1);
        FillCurrentBrushPen(drawRect);
        }
    }

EXPORT_C void CConUiSurface::DrawLine(const TPoint& aFrom, const TPoint& aTo, TLineStyle aStyle)
    {
    FL(_L("CConUiSurface::DrawLine"));

    }

EXPORT_C void CConUiSurface::DrawRect(const TRect& aRect, TLineStyle aStyle)
    {
    FL(_L("CConUiSurface::DrawRect"));
    TRect inBounds = Rect();
    inBounds.Intersection(aRect);
    // filling inner space
    inBounds.Move(1, 1);
    inBounds.Shrink(2, 2);
    FillCurrentBrushPen(inBounds);
    // drawing outline
    /**
     * topLeft----------------------topRight
     *    |                             |
     *    |                             |
     * bottomLeft------------------bottomRight
     */
    inBounds.Move(-1, -1);
    inBounds.Grow(2, 2);
    const TPoint& topLeft = inBounds.iTl;
    const TPoint& bottomRight = inBounds.iBr;
    TPoint topRight(bottomRight.iX, topLeft.iY);
    TPoint bottomLeft(topLeft.iX, bottomRight.iY);
    // top side
    DrawLine(topLeft, topRight - TPoint(1, 0), aStyle);
    // right side
    DrawLine(topRight, bottomRight - TPoint(0, 1), aStyle);
    // bottom side
    DrawLine(bottomRight, bottomLeft + TPoint(1, 0), aStyle);
    // left side
    DrawLine(bottomLeft, topLeft + TPoint(0, 1), aStyle);
    }

EXPORT_C void CConUiSurface::InvalidateL(const TRect& aRect)
    {

    }

EXPORT_C void CConUiSurface::ValidateL(const TRect& aRect)
    {

    }

EXPORT_C void CConUiSurface::Blit(const CConUiSurface& aSource, const TRect& aRect)
    {

    }

EXPORT_C void CConUiSurface::Blit(const CConUiSurface& aSource, const RRegion& aRegion)
    {

    }

EXPORT_C void CConUiSurface::ShowCursor(const TPoint& aPosition)
    {

    }

EXPORT_C TInt CConUiSurface::CursorPos(TPoint& aPosition) const
    {

    }

EXPORT_C TInt CConUiSurface::HideCursor()
    {

    }

TInt CConUiSurface::ValidateCallback(TAny* aSelf)
    {
    FL(_L("CConUiSurface::ValidateCallback"));
    CConUiSurface* self = reinterpret_cast<CConUiSurface*>(aSelf);
    TRAPD(err, self->DoValidateCallbackL());
    return err;
    }

void CConUiSurface::DoValidateCallbackL()
    {
    FL(_L("CConUiSurface::DoValidateCallbackL"));
    // TODO: implement CConUiSurface::DoValidateCallbackL
    }

void CConUiSurface::FillCurrentBrushPen(const TRect& aRect)
    {
    // setting pen effect
    if (EPenEffectPreserve != iPen.Effect())
        {
        // setting blinking
        TPenEffect effect = iPen.Effect();
        if (EPenEffectBold == effect)
            {
            iBuffer->FillBold(aRect, ETrue);
            iBuffer->FillBlinking(aRect, EFalse);
            }
        else if (EPenEffectBlinking == effect)
            {
            iBuffer->FillBold(aRect, EFalse);
            iBuffer->FillBlinking(aRect, ETrue);
            }
        else if (EPenEffectBoldBlinking == effect)
            {
            iBuffer->FillBold(aRect, ETrue);
            iBuffer->FillBlinking(aRect, ETrue);
            }
        }
    // setting background
    if (EColorPreserve != iBrush.Color())
        {
        ConUiDef::TColor color = TranslateColor(iBrush.Color());
        iBuffer->FillBackground(aRect, color);
        }
    // setting foreground
    if (EColorPreserve != iPen.Color())
        {
        ConUiDef::TColor color = TranslateColor(iPen.Color());
        iBuffer->FillForeground(aRect, color);
        }
    // setting inverse video
    if (EBrushEffectPreserve != iBrush.Effect())
        {
        TBool invert = EBrushEffectInvesrseVideo == iBrush.Effect();
        iBuffer->FillInverseVideo(aRect, invert);
        }
    }
